package work.linruchang.util;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import io.jsonwebtoken.*;

import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Date;
import java.util.Map;

/**
 * 增强的JWT处理工具
 *
 * @author LinRuChang
 * @version 1.0
 * @date 2020/09/25
 * @since 1.8
 **/
public class EnhanceJwtUtil {

    /**
     * 签名使用RSA进行加密
     * @param payload   负载信息 - json字符串
     * @param privateKey  签名秘钥 - 私钥
     * @param addExpirationDay  秘钥过期增加的天数
     * @return
     */
    public static String createToken(Map<String,? extends Object> payload, PrivateKey privateKey, Integer addExpirationDay) {
        JwtBuilder jwtBuilder = Jwts.builder();

        //1. token页眉部分信息 - 定义尾部签名算法
        jwtBuilder.setHeaderParam("alg", "RS512");
        jwtBuilder.setHeaderParam("type", "JWT");

        //2. token负载中间部分信息
        jwtBuilder.setClaims((Map<String, Object>) payload);
        //过期时间 - 该行代码必须放在setClaims后面
        jwtBuilder.setExpiration(DateUtil.offset(new Date(), DateField.DAY_OF_MONTH, addExpirationDay));

        //3. token尾部 - 负载中间部分签名算法、签名秘钥
        jwtBuilder.signWith(SignatureAlgorithm.RS512, privateKey);

        return jwtBuilder.compact();
    }

    /**
     * 验证token字符串是否符合要求 - 如果签名不正确则返回false,正确则返回true
     * @param token
     * @param publicKey  公钥
     * @return
     */
    public static Boolean verifyToken(String token, PublicKey publicKey) {
        try {
            Jwt parse = Jwts.parser().setSigningKey(publicKey).parse(token);
            return true;
        }catch (Exception e) {

            return false;
        }
    }

    /**
     * 获取token负载的信息
     * @param token
     * @param publicKey  公钥
     * @return
     */
    public static Map<String, Object> getTokenBody(String token, PublicKey publicKey) {
        try {
            Claims body = Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token).getBody();
            return body;
        }catch (Exception e) {

            return null;
        }
    }


    /**
     * 获取token内容里面某个字段的信息
     * @param token
     * @param bodyName  字段名
     * @param publicKey  公钥
     * @return
     */
    public static String getMessage(String token,String bodyName, PublicKey publicKey) {
        try {
            Claims body = Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token).getBody();
            return (String) body.get(bodyName);
        }catch (Exception e) {

            return null;
        }
    }

    /**
     * 将token里面的body健值，转成某个对象【targetClazz】
     * @param token
     * @param publicKey
     * @param targetClazz
     * @param keyMaps  属性值的映射  body里面的key不一定是完全一摸一样targetClazz里面的属性，故需要有这个映射来确保映射准备，当然也可以不用
     * @param <T>
     * @return
     */
    public static <T> T parseBody(String token, PublicKey publicKey, Class<T> targetClazz, Map<String,String> keyMaps) {
        try {
            Claims body = Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token).getBody();
            //return ReflectUtil.parseMap2Bean(body, targetClazz, keyMaps);
            return BeanUtil.mapToBean(body, targetClazz, true);
        }catch (Exception e) {

            return null;
        }
    }

    /**
     * 将token里面的body健值，转成某个对象【targetClazz】
     * @param token
     * @param publicKey
     * @param targetClazz
     * @param <T>
     * @return
     */
    public static <T> T parseBody(String token, PublicKey publicKey, Class<T> targetClazz) {
        return parseBody(token, publicKey, targetClazz, null);
    }

    public static String getBodyValueByKey(String token, PublicKey publicKey, String key) {
        Claims body = Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token).getBody();
        return (String) body.get(key );
    }

}
